import uuid
import os
import subprocess
from loguru import logger
from tybase.tools.video_utils import get_audio_duration
import sys
sys.path.append("..")   #也可以这样
sys.path.append("../..")   #也可以这样
from config import kSlideOutputPath
from utils import cloud_utils
from tybase.tools.down_tools import FileDownloader
from tybase.tools.image_utils import delete_file_or_directory

from utils import cloud_utils
from utils.image_utils import crop_image_list
from PIL import Image
import os

def replace_image_paths(image_list_data, images_info):
    # 获取file_paths列表
    file_paths = images_info['file_paths']

    # 遍历image_list_data并替换图片地址
    for i, image_data in enumerate(image_list_data):
        image_data["图片地址"] = file_paths[i]

    return image_list_data


# 根据图片列表生成FFmpeg命令
def generate_final_ffmpeg_command(image_list, output_filename):
    # Initialize command parts
    input_cmds = []
    filter_complex_cmds = []
    offset = 0  # Initialize offset

    # Iterate over the image list to construct the input commands
    for index, image in enumerate(image_list):
        input_cmd = f"-loop 1 -t {image['持续时间']} -i {image['图片地址']}"
        input_cmds.append(input_cmd)

    # Construct the filter complex commands
    for index in range(len(image_list) - 1):
        transition_effect = image_list[index + 1]['转场效果']
        filter_name = f"bg{index + 1}" if index else "bg1"

        offset += image_list[index]['持续时间'] - image_list[index]['转场时间']

        if index == 0:
            filter_complex_cmd = f"[0][1]xfade=transition={transition_effect}:duration={image_list[index + 1]['转场时间']}:offset={offset}[{filter_name}]"
        else:
            previous_filter_name = f"bg{index}"
            filter_complex_cmd = f"[{previous_filter_name}][{index + 1}]xfade=transition={transition_effect}:duration={image_list[index + 1]['转场时间']}:offset={offset}[{filter_name}]"

        filter_complex_cmds.append(filter_complex_cmd)

    # Modify the last filter to remove the label
    last_filter = filter_complex_cmds[-1]
    last_filter = last_filter.rsplit("[", 1)[0] + ",format=yuv420p"
    filter_complex_cmds[-1] = last_filter

    # Construct the final command
    ffmpeg_command = f"ffmpeg {' '.join(input_cmds)} -filter_complex \"{'; '.join(filter_complex_cmds)}\" {output_filename} -y"

    return ffmpeg_command


# TODO: 封装
def trim_audio(audio_path, duration, output_path):
    """
    Trim an audio file to the specified duration.
    """
    filter_complex = f"[0:a]atrim=duration={duration}[aout]"

    cmd = ["ffmpeg", "-i", audio_path, "-filter_complex", filter_complex, "-map", "[aout]", output_path]
    try:
        subprocess.check_output(cmd, stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError as e:
        print("Error during audio trimming:")
        print(e.output.decode())
        return None

    return output_path


# TODO: 封装,创建循环音频
def loop_audio_without_remainder(audio_path, loop_times, output_path):
    """
    Loop an audio file for the specified number of times without considering the remaining duration.
    """
    input_flags = ["-i", audio_path] * loop_times
    concat_inputs = ''.join([f"[{i}:a]" for i in range(loop_times)])
    filter_complex = f"{concat_inputs}concat=n={loop_times}:v=0:a=1[aout]"

    cmd = ["ffmpeg"] + input_flags + ["-filter_complex", filter_complex, "-map", "[aout]", output_path]
    try:
        subprocess.check_output(cmd, stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError as e:
        print("Error during audio looping without remainder:")
        print(e.output.decode())
        return None

    return output_path


# TODO: 封装,合并2个音频文件
def merge_two_audio_files(looped_audio_path, trimmed_audio_path, output_path):
    """
    Merge two audio files: a looped audio and a trimmed audio.
    """
    filter_complex = f"[0:a][1:a]concat=n=2:v=0:a=1[aout]"

    cmd = ["ffmpeg", "-i", looped_audio_path, "-i", trimmed_audio_path, "-filter_complex", filter_complex, "-map",
           "[aout]", output_path]
    try:
        subprocess.check_output(cmd, stderr=subprocess.STDOUT)
    except subprocess.CalledProcessError as e:
        print("Error during merging two audio files:")
        print(e.output.decode())
        return None

    return output_path


def generate_complete_looped_audio(audio_path, target_duration, output_path):
    """
    创建一个循环的音频文件,与主文件保持一致
    """
    # 获取音频的时长
    audio_duration = get_audio_duration(audio_path)  # 假如是9秒
    print("音频的时长为%s秒" % audio_duration)

    if target_duration < audio_duration:
        return trim_audio(audio_path, target_duration, output_path)

    # 计算音频应循环的次数和剩余部分
    loop_times = int(target_duration / audio_duration)
    remaining_duration = target_duration - (loop_times * audio_duration)

    # 生成唯一的临时文件名
    looped_audio_temp_path = f"temp_looped_audio_{uuid.uuid4()}.wav"
    trimmed_audio_temp_path = f"temp_trimmed_audio_{uuid.uuid4()}.wav"

    # 创建循环的音频
    loop_audio_without_remainder(audio_path, loop_times, looped_audio_temp_path)

    # 创建截取的音频部分
    trim_audio(audio_path, remaining_duration, trimmed_audio_temp_path)

    # 合并循环的音频和截取的部分
    merge_two_audio_files(looped_audio_temp_path, trimmed_audio_temp_path, output_path)

    # 清除临时文件
    os.remove(looped_audio_temp_path)
    os.remove(trimmed_audio_temp_path)
    # delete_file_or_directory(images_info["directory"])  # 删除图片文件夹

    return output_path


def merge_audio_video(audio_path, video_path, output_path="merged_output.mp4"):
    """
    Merge the provided audio and video files using ffmpeg, if an audio path is provided.
    If no audio path is provided, only the video file is processed.

    Parameters:
    - audio_path: Path to the audio file.
    - video_path: Path to the video file.
    - output_path: Path for the merged output file (default is "merged_output.mp4").

    Returns:
    - Path to the processed output file.
    """

    if audio_path:
        # Command to merge audio and video
        cmd = [
            'ffmpeg',
            '-i', video_path,  # Input video path
            '-i', audio_path,  # Input audio path
            '-c:v', 'copy',  # Copy video codec settings
            '-c:a', 'aac',  # Use AAC codec for audio
            '-strict', 'experimental',
            '-shortest',  # Output as long as the shortest input
            '-y',
            output_path  # Output file
        ]
    else:
        # Command to process only the video
        cmd = [
            'ffmpeg',
            '-i', video_path,  # Input video path
            '-c:v', 'copy',  # Copy video codec settings
            '-an',  # No audio
            '-y',
            output_path  # Output file
        ]

    subprocess.run(cmd, check=True)

    return output_path


def start(image_list_data, audio_path, all_time, output_filename):
    """
    :type image_list_data: 图片信息列表
    """
    video_output_filename = f"{uuid.uuid4()}.mp4"
    # 生成一个视频的临时的文件
    ffmpeg_command_corrected = generate_final_ffmpeg_command(image_list_data, video_output_filename)
    os.system(ffmpeg_command_corrected)

    
    if not audio_path:
        # 在没有传入音频的情况下,就不走下载音频的处理逻辑
        audio_path_all = ""
    else:
        audio_output_filename = f"{uuid.uuid4()}.wav"
        audio_path_all = generate_complete_looped_audio(audio_path, all_time, audio_output_filename)
        # 这里的音频文件可以不修改的,需要标注一下
    try:
        obj_path = merge_audio_video(audio_path_all, video_output_filename, output_filename)
    except Exception as err:
        logger.error(f"合成视频失败,错误信息:{err}")
        raise Exception("在[merge_audio_video] 失败")
    finally:
        # 把临时的视频和音频删除
        if audio_path_all:
            os.remove(audio_path_all)
        os.remove(video_output_filename)

    return obj_path


def translate_keys(image_list_data):
    # 定义key的翻译映射
    translation_map = {
        "image_url": "图片地址",
        "transition_time": "转场时间",
        "effect": "转场效果",
        "duration": "持续时间"
    }
    # # 如果传入的 image_list_data 是 dict就不做下面的转换
    # if isinstance(image_list_data, dict):
    #     image_list_data = [item.dict() for item in image_list_data]
    # else:
    #     pass
    # # 使用映射来翻译每个数据字典中的key
    image_list_data = [item.dict() for item in image_list_data]
    
    translated_list = []
    for data in image_list_data:
        translated_data = {}
        for key, value in data.items():
            translated_key = translation_map.get(key, key)  # 如果没有对应的翻译，就使用原key
            translated_data[translated_key] = value
        translated_list.append(translated_data)

    return translated_list


import shutil

def remove_folder(folder_path):
    try:
        shutil.rmtree(folder_path)
        print(f"文件夹 {folder_path} 已成功删除。")
    except OSError as e:
        print(f"删除文件夹 {folder_path} 失败: {e}")

# 单个视频图片的合成逻辑
def create_video_from_image_with_audio(image_path, duration, output_path, audio_path=''):
    """
    Create a video from a single image, optionally adding audio.

    Parameters:
    - image_path: Path to the image file.
    - duration: Duration of the video.
    - output_path: Path for the output video file.
    - audio_path: Optional path to the audio file.

    Returns:
    - Path to the output video file.
    """

    if audio_path:
        # Command to create video with audio
        cmd = f"ffmpeg -loop 1 -i {image_path} -stream_loop -1 -i {audio_path} -c:v libx264 -c:a aac -shortest -t {duration} {output_path}"
    else:
        # Command to create video without audio
        cmd = f"ffmpeg -loop 1 -i {image_path} -c:v libx264 -t {duration} -pix_fmt yuv420p {output_path}"

    subprocess.call(cmd, shell=True)
    return output_path

# 将音频转成wav格式
def convert_to_wav(audio_path, wav_output_path):
    cmd = f"ffmpeg -i {audio_path} {wav_output_path}"
    subprocess.call(cmd, shell=True)
    return wav_output_path



# 重新格式化的保存png
def convert_png_to_jpg(image_path):
    if image_path.lower().endswith('.png'):
        img = Image.open(image_path)
        jpg_path = image_path[:-4] + '.jpg'
        rgb_im = img.convert('RGB')
        rgb_im.save(jpg_path)
        return jpg_path
    else:
        return image_path


def main(image_list_data, audio_url):
    image_list_data = translate_keys(image_list_data)  # 先把参数名称转成中文

    # 下载图片到本地
    downloader_imags = FileDownloader(save_dir="./downloads/images")
    images_paths_urls = [item["图片地址"] for item in image_list_data]  # 从url中提取出图片地址
    images_info = downloader_imags.download_files(images_paths_urls)
    image_list_data = replace_image_paths(image_list_data, images_info)
    try:
        # 下载音频到本地
        downloader_audio = FileDownloader(save_dir="./downloads/audio")
        if audio_url:

            audio_path = downloader_audio.download_file(audio_url)
        else:
            audio_path = ""
        
        
        # 对图像进行统一的裁剪
        # 提取所有的图片
        image_list_path =  [item["图片地址"] for item in  image_list_data]
        # 进行统一的裁剪(新增)
        crop_image_list(image_list_path)
        
        
        # 这里需要做一下处理,如果是单张图片的话,直接就进行拼接
        # 这里是把所有的图片list拼接成一个地址
        outptut_file_md5 = downloader_audio.calculate_md5_for_string(audio_path + ','.join(images_paths_urls))
        
        import uuid
        random_uuid = str(uuid.uuid4())
        output_file = f"{outptut_file_md5}_{random_uuid}.mp4"
        all_time = sum([i['持续时间'] for i in image_list_data])
        
        # 这里开始做逻辑判断,单张图片和多张图片的处理逻辑,单张图片的逻辑
        if len(image_list_data) == 1:
            image_path = image_list_data[0]["图片地址"]
            image_path = convert_png_to_jpg(image_path)
            duration = image_list_data[0]["持续时间"]

            if audio_path:
                # 生成一个uuid的字符串
                import uuid
                uuid_str = str(uuid.uuid4())
                wav_path = f"{uuid_str}" + ".wav"
                convert_to_wav(audio_path, wav_path)
                audio_path =  wav_path
                create_video_from_image_with_audio(image_path, duration, output_file, audio_path = audio_path)
            else:
                # 没有音频地址的处理逻辑
                create_video_from_image_with_audio(image_path, duration, output_file, audio_path)
            from loguru import logger
            logger.info(f"单张图片的合成逻辑,图片地址:{image_path}, 音频地址:{audio_path}, 输出地址:{output_file}")

        else:
            # 多个图片的合成逻辑
            start(image_list_data, audio_path, all_time, output_file)
        # 上传阿里云的逻辑
        ali_cloud_path = cloud_utils.transform_path(output_file, "Slides")
        aliyun_res = cloud_utils.upload_file_to_aliyun(ali_cloud_path, output_file)
        
    finally:
        # 删除所有缓存的文件夹
        if audio_path:
            os.remove(audio_path)
        delete_file_or_directory(images_info["directory"])
    return aliyun_res


if __name__ == '__main__':
    # 需要把音频和图片的地址给构建出来
    from tybase.tools.down_tools import FileDownloader

    # audio_url = "https://ty-huanlian.oss-cn-shanghai.aliyuncs.com/output_audio.mp3"
    audio_url = "http://scfile.chaotuapp.com/newpreview_music/231214/23beee929cbc492efe580b76367b2f5c.mp3"
    # 这里可以单独进行测试
    # image_list_data = [
    #     {"image_url": "https://ty-huanlian.oss-cn-shanghai.aliyuncs.com/test/image1.jpg",
    #      "transition_time": 0.7,
    #      "duration": 2,
    #      "effect": "fade"},
    #     {"image_url": "https://ty-huanlian.oss-cn-shanghai.aliyuncs.com/test/image2.jpg", "transition_time": 0.7,
    #      "duration": 3,
    #      "effect": "wipeup"},
    #     {"image_url": "https://ty-huanlian.oss-cn-shanghai.aliyuncs.com/test/image3.jpg", "transition_time": 0.7,
    #      "duration": 3,
    #      "effect": "fade"}
    # ]
    
    image_list_data = [{"effect": "fade", "duration": 3.0, "image_url": "https://file.xiaolubi.com/upload/sync/e95756e23838385bb3d2728e9f5eefd9.jpg", "transition_time": 0.7}, {"effect": "fade", "duration": 3.0, "image_url": "https://i.xiaolubi.com/draw/20231220/a76f49a9-5e17-4f15-aa30-afe37087d667.jpg", "transition_time": 0.7}]
    
    print(main(audio_url=audio_url, image_list_data=image_list_data))
    
    # Next: 如何做成后台通知的模式
